package sam.utilities.xml; /* This module is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as publised by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This module is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA Should you need to contact the author, you can do so by email to <wsh@sprintmail.com>. */ import java.util.Vector; import java.util.Stack; import java.io.PrintWriter; import java.io.FileReader; import java.io.BufferedReader; import java.io.IOException; import java.io.Serializable; /** * <p>Title: XML parser/generator</p> * <b><p>Description: WEB applet for X10 Linux Universal Device Drivers<br> * Copyright: Copyright (c) 2003<br> * License: GPL<br> * Summary:This is a poor man's version of a DOM like XML reader/writer written specifically so that it is * very small and ideal for an applet.</p></b> * <p>The format supported is as followed:</p> * <p><?xml version="1.0" standalone="yes"?><br> * <!-- Comment --><br> * <rootname><br> *   <tag><br> *     <tag attribute1="value" attribute2="value">elementvalue</tag><br> *   </tag><br> * </rootname></p> * <p> The following are not supported:</p> * <ul> * <li> Processing Instructions * <li> Namespace * </ul> * <hr> * <p> How to create XML tree in memory to write to disk:</p> * <ol> * <li>Create the root node with a name (myXML root = new myXML("roottag");) * <li>Create subelements * <ol> * <li>If subelement will have subelements (branch), use the form without a value * (myXML subelement = roottag.addElement("subelement");) * <li>If the subelement will not have subelements (leaf), add the element with a value * (myXML subelement = roottag.addElement("element",value);) * <li>For each subelement (branch and leaf) add attributes * (subelement.Attribute.add("name",value);) or (root.Attribute.add("name",value);) * </ol> * <li>Continue this until you have completed the tree * <li>Write it to a PrintWriter stream to generate XML * </ol> * <p> Example: </p> * <ul> * <li> myXML root = new myXML("root"); * <li> myXML sub1 = root.addElement("element1",value1); * <li> sub1.Attribute.add("attribute1",att1); * <li> sub1.Attribute.add("attribute2",att2); * <li> myXML sub2 = root.addElement("element2"); * <li> sub2.Attribute.add("attribute20",att20); * <li> myXML sub21 = sub1.addElement("element21"); * <li> sub21.Attribute.add("attribute21",value21); * <li> root.serialize(pw); * </ul> * <p> Results in the following:</p> * <p> * <?xml version="1.0" standalone="yes"?><br> * <root><br> *   <element1 attribute1="att1" attribute2="att2">value1</element1><br> *   <element2 attribute20="att20"><br> *     <element21 attribute21="att21">value21</element21><br> *   </element2><br> * </root> * </p> * <p> Values for Elements and Attributes can be any type of object but will be * converted to String by calling toString() before storing the value.</p> * <hr> * <p> How to load XML tree from disk:</p> * <ol> * <li> Open a BufferedReader (in = new BufferedReader(file);) * <li> Create a new myXML(BufferedReader) object passing it the BufferedReader (xmlroot = new myXML((BufferedReader)in);) * </ol> * <hr> * <p> How to walk through the XML tree:</p> * <ul> * <li> myXML.getTag(): Returns the tag name of the myXML object * <li> myXML.getValue(): Returns the value of the myXML object * <li> myXML.getElement(int): Returns the myXML sub-object at the specified index * <li> myXML.findElement(String tag): Returns the first myXML object with the name tag * <li> myXML.findElement(String tag, Attribute name): Returns the first myXML object with the name tag and attribute name * <li> myXML.Attribute.find(String name): Returns the attribute value associated with name * </ul> * @author Scott Hiles * @version 1.1.0 */ public class myXML implements Serializable { private final boolean debug=false; private final String XMLheader = "<?xml version=\"1.0\" standalone=\"yes\"?>"; private String XMLcomment = ""; private String tag; private Object element = null; // either an Object or a Vector() public Attribute Attribute = new Attribute(); /** * Create new myXML object with no subelements, no attributes, and no value * * @param tag initial name for the XML element or null for empty element */ public myXML(String tag) { this.tag = tag; if (debug) System.out.println("myXML("+tag+")"); } public myXML() { } public void setXMLComment(String comment){ this.XMLcomment = comment; } /** * Retrieve the name/tag * * @return <b>null</b> if no name set<br> * <b>String object</b>: for the text of the XML element */ public String getTag() { return this.tag; } /** * Retrieve the data value for this element in String form * * @return <b>null</b> if no value set<br> * <b>String object</b>: for the text of the XML element */ public String getValue() { if (element == null) return null; if (this.element.getClass() == Vector.class) return null; return(String)this.element; } /** * Retrieve the number of elements attached to this element * * @return int value */ public int size() { if (element == null) return 0; if (element.getClass() == Vector.class) return((Vector)element).size(); return 0; } /** * Find and retrieve the first element that has the specified tag name * The search starts with this object and follows all branch and leaf nodes * in the XML tree. * * @return <b>null</b>: no object with the specified tag found<br> * <b>myXML object</b>: First object found with the matching tag */ public myXML findElement(String tag) { if (this.tag.compareTo(tag) == 0) return this; if (element == null) return null; if (element.getClass() != Vector.class) return null; myXML xml = null; Vector v = (Vector)this.element; int i; for (i = 0; i < v.size(); i++) { xml = (myXML)v.get(i); if (xml == null) continue; if (xml.findElement(tag) != null) return xml; } return null; } /** * Remove subelement (element) from list of objects for a parent element * * @param element element to be removed from object list of parent * * @return boolean value set to true if the element was found and removed from the parent */ public boolean removeElement(myXML element) throws myXMLException { if (element == null) throw new myXMLException("Cannot prune if target element is null"); if (this.element == null) throw new myXMLException("No subelements"); if (this.element.getClass() != Vector.class) throw new myXMLException("No subelements"); Vector v = (Vector)this.element; int i; if (v.size() <= 0) return false; if (!v.remove(element)) return false; if (v.size() == 0) // go ahead and cl this.element = null; return true; } /** * Tests if the specified element is a subelement of this element * * @param element element to test * * @return boolean value set to true if element is found to be subelement */ public boolean contains(myXML element) throws myXMLException { if (element == null) throw new myXMLException("Cannot prune if target element is null"); if (this.element == null) throw new myXMLException("No subelements"); if (this.element.getClass() != Vector.class) throw new myXMLException("No subelements"); Vector v = (Vector)this.element; int i; if (v.size() <= 0) return false; return v.contains(element); } /** * Removes all subelements from the specified parent * * @return boolean value set to true if all subelements removed */ public boolean removeAllElements() { if (this.element == null) return false; if (this.element.getClass() != Vector.class) return false; element = null; return true; } /** * Tests if this parent has no subelements * * @return boolean value set to true if size is zero */ public boolean isEmpty() { return(this.element == null); } /** * Find and retrieve the first element that has the specified attribute name. * The search starts with this object and follows all branch and leaf nodes * in the XML tree. * * @return <b>null</b>: No element found with the specified attribute name<br> * <b>myXML object</b>: First object found with the matching attribute name */ public myXML findattribute(String name) { if (Attribute.find(name) != null) return this; if (element == null) return null; if (element.getClass() != Vector.class) return null; myXML xml = null; Vector v = (Vector)element; int i; for (i = 0; i < v.size(); i++) { xml = (myXML)v.get(i); if (xml.findattribute(name) != null) return xml; } return null; } /** * Find and retrieve the first element that has the specified tag name and * the specified attribute name. The search starts with this object and * follows all branch and leaf nodes in the XML tree. * * @return <b>null</b>: No element found with the specified tag name and attribute name<br> * <b>myXML object</b>: First object found matching both the tag name and attribute name */ public myXML findElement(String tag, String attname) { if (this.tag.compareTo(tag) == 0 && this.Attribute.find(attname) != null) return this; if (element == null) return null; if (element.getClass() != Vector.class) return null; myXML xml = null; Vector v = (Vector)element; int i; for (i = 0; i < v.size(); i++) { xml = (myXML)v.get(i); if (xml.findElement(tag) != null && xml.Attribute.find(attname) != null) return xml; } return null; } /** * Retrieve the element object at the specified <b>index</b>. The branche and * leaf nodes in the XML tree are stored in a Vector and each index represents * a node in the tree. Indicies start at 0 and count up to the length of the * Vector. * * @param index integer indicating the branch or leaf of the this XML object * * @return <b>null</b>: if index is out of bounds<br> * <b>myXML object</b>: element at the specified index. */ public myXML getElement(int index) { if (element == null) return null; if (element.getClass() != Vector.class) return null; Vector v = (Vector)element; if (index > v.size()) return null; if (index < 0) return null; return(myXML)v.get(index); } /** * Set the tag for this element (private) * * @param tag String to use for tag name for element */ private void setTag(String tag) { this.tag = tag; if (debug) System.out.println("myXML.setTag("+tag+")"); } /** * Set the value for this element (private) * * @param value String to use for value for the element */ private void setValue(String value) throws myXMLException { if (element != null) if (element.getClass() == Vector.class) throw new myXMLException("Cannot assign a value to an element with subelements"); this.element = value; if (debug) System.out.println("myXML.setValue("+value+")"); } /** * Add a subelement which will be used as a branch in the XML tree to the * this element of the form:<br> * <br><tag attr1="attr1value attr2="attr2value" ... ><br> * ...<br> * </tag> * * @param tag String used to identify the element * * @return <b>myXML object</b>: the newly created element */ public myXML addElement(String tag) throws myXMLException { Vector v = null; if (element != null) if (element.getClass() != Vector.class) throw new myXMLException("Cannot create a subelement to an element with a value"); if (element == null) { v = new Vector(); // create the new subelement element = v; } else v = (Vector)element; myXML xml = new myXML(tag); v.add((Object)xml); if (debug) System.out.println("myXML.addElement("+tag+")"); if (debug) System.out.println("elements.size() = "+v.size()); return xml; } /** * Add a leaf element to the XML tree of the form:<br> * <br><tag attr1="attr1value attr2="attr2value" ... >value</tag> * * @param tag String used to identify the element * @param value Object to use for the element value (Note that the object will * be converted to a String before storing in the XML tree. * * @return <b>myXML object</b>: the newly created element */ public myXML addElement(String tag,Object value) throws myXMLException { Vector v = null; if (element != null) if (element.getClass() != Vector.class) throw new myXMLException("Cannot create a subelement to an element with a value"); if (element == null) { v = new Vector(); element = v; } else v = (Vector)element; myXML e = new myXML(tag); e.setValue(value.toString()); v.add(e); if (debug) System.out.println("myXML.addElement("+tag+","+value+")"); if (debug) System.out.println("elements.size() = "+v.size()); return e; } // ----------------------------------------------------------------------------- /** * Attributes associated with element tags. Multiple attributes can be added * to any element tag and will be stored between the < and >. Attributes * are written to the XML tree in the form:<br> * <br><tag attr1="attr1value attr2="attr2value" ... >value</tag> */ public class Attribute { private Vector attributes = new Vector(); /** * Add a named attribute to this element * * @param name String identifying the name of the attribute * @param value Object identifying the data to be associated with the attribute */ public void add(String name, Object value) { attribute a = new attribute(name, value.toString()); attributes.add( (Object) a); if (debug) System.out.println("myXML.Attribute.add("+name+","+value+")"); } private String dumpattributes() { int i; String result = new String(""); for (i = 0; i < attributes.size(); i++) { attribute o = (attribute)attributes.get(i); result = new String(result+" "+o.name+"=\""+cleanup(o.value)+"\""); } return result; } /** * Retrieve the value associated with the attribute identified * * @param name The name of the attribute to find * * @return <b>String object</b>: String form of the value associated with the attribute */ public String find(String name) { int i; if (attributes.size() == 0) return null; for (i = 0; i < attributes.size(); i++) { attribute a = (attribute)attributes.get(i); if (a.name == null) continue; else if (a.name.compareTo(name) == 0) return a.value; } return null; } private class attribute { public String name = null; public String value = null; attribute(String name,String value){ this.name = name; this.value = value; } } } // ----------------------------------------------------------------------------- // XML writer (serializer) // ----------------------------------------------------------------------------- // Simply recurse through the tree printing out the tags, attributes, and // subelements as you go. Every node in the tree is an instance of myXML // so recursion is fairly trivial. // // This is all original code, so don't stone me if I have a mistake. // ----------------------------------------------------------------------------- /** * Write the XML tree to an output stream in text form. * * @param out A PrintWriter object to be used as the output stream for the XML tree. */ public void serialize(PrintWriter out) throws IOException { int depth = 0; out.println(XMLheader); out.println(XMLcomment); dumpelements("",out); } private void dumpelements(String indent,PrintWriter out) { int i; out.print(indent+"<"+tag+Attribute.dumpattributes()); if (element == null) { out.println("/>"); return; } if (element.getClass() == Vector.class) { Vector v = (Vector)element; out.println(">"); for (i = 0; i < v.size(); i++) ((myXML)v.get(i)).dumpelements(new String(indent+" "),out); out.println(indent+"</"+tag+">"); } else { // we have children so we can't have any elements out.println(">"+getValue()+"</"+tag+">"); } } // Here is a cute little method that you have to use before you write any // user specified names out. If the user embeds the characters &, <, >, ", or ' // in the name of an object and it is written as is, the XML reader will choke. // // The order that the characters are replaced is very important in so much as // the & character must be replaced first since the other replacements add // & characters. // // This method is private as it is only needed as you serialize the objects. // The application (user) shouldn't have to worry about the characters that // are used in the application or entered by the user. private Object cleanup(Object o) { if (o.getClass() == String.class) { String s = (String)o; s = s.replaceAll("&","&"); s = s.replaceAll("<","<"); s = s.replaceAll(">",">"); s = s.replaceAll("\"","""); s = s.replaceAll("\'","'"); s = s.replaceAll("/","/"); // s = s.replaceAll("?","?"); s = s.replaceAll("!","!"); return s; } return o; } // ----------------------------------------------------------------------------- // XML parser/reader // ----------------------------------------------------------------------------- // The XML reader/parser state machine technique was extracted from // Steven Brandt's article in JavaWorld // (http://www.javaworld.com/javaworld/javatips/jw-javatip128.html) // // Steven's state machine makes calls like a SAX parser; however, this // adaption acts more like a DOM parser to load the entire document into // memory exactly in the form that the writer expects so that the application // can query the object tree to find information. Variable names and // programming technique differs from Steven's so that I could figure out // what his state machine was trying to do. // // All values for Elements and Attributes are stored as String objects and // Should be interpreted by the application. // ----------------------------------------------------------------------------- /** * Read the XML tree in from the specified stream. The stream can be anything * that can be opened as a BufferedReader which includes URLs and files. The XML * tree will be stored in a tree of myXML objects and the root object will be returned. * * @param in BufferedReader input stream */ public myXML(BufferedReader in) throws myXMLException, myXMLEncodingException, IOException { myFileReader f = new myFileReader(in); try { readXML(this, f); } catch (myXMLEncodingException e) { throw e; } catch (IOException e) { throw e; } catch (myXMLException e) { throw e; } if (debug) { System.out.println("XML file successfully loaded"); // System.exit(0); } } private static int popState(Stack st) { if (!st.empty()) return((Integer)st.pop()).intValue(); else return PRE; } private final static int // Steven Brandt's original states TEXT = 1, ENTITY = 2, OPEN_TAG = 3, CLOSE_TAG = 4, START_TAG = 5, ATTRIBUTE_LVALUE = 6, ATTRIBUTE_EQUAL = 9, ATTRIBUTE_RVALUE = 10, QUOTE = 7, IN_TAG = 8, SINGLE_TAG = 12, COMMENT = 13, DONE = 11, DOCTYPE = 14, PRE = 15, CDATA = 16; private int line = 1, col = 0; private boolean eol = false; // This is a very simple method which allows me to unread characters that // have been read from the file stream. This one is critical to the XMLreader // so that you can find the <tag> markers and push the "<" back into the reader // to allow you to recursively call the reader as if the <tag> were a new root // object. private class myFileReader implements Serializable { Stack stack = new Stack(); BufferedReader f = null; myFileReader(BufferedReader in) { f = in; } void unread(int c) { stack.push(new Integer(c)); } int read() throws IOException { if (stack.empty()) try { return f.read(); } catch (IOException e) { throw e; } else { int c = ((Integer)stack.pop()).intValue(); return c; } } } // Steven's code had this embedded but I took it out to clean up the recursive // method. This just takes out the linefeeds and carriage returns private boolean checkforlinefeed(int c) { // We need to map \r, \r\n, and \n to \n // See XML spec section 2.11 if (c == '\n' && eol) { eol = false; return true; } else if (eol) { eol = false; } else if (c == '\n') { line++; col = 0; } else if (c == '\r') { eol = true; c = '\n'; line++; col = 0; } else { col++; } return false; } //------------------------------------------------------------------------------ // OK, here is your workhorse for the parser. It implements the same kind of // character reader based state machine that Steven Brandt demonstrated in // http://www.javaworld.com/javaworld/javatips/jw-javatip128.html. But, the // significant change here is that the reader calls itself recursively to // build the document tree and doesn't make document handler calls. The // states have been reordered and turned into a switch() statement but they // behave mostly the same. //------------------------------------------------------------------------------ private void readXML(myXML xml,myFileReader in) throws myXMLException, myXMLEncodingException, IOException { int c; int state = PRE; Stack stack = new Stack(); StringBuffer sb = new StringBuffer(); StringBuffer etag = new StringBuffer(); String lvalue = null, rvalue = null; int quotec = '"'; if (debug) System.out.println("readXML starting"); while ((c = in.read()) != -1) { if (checkforlinefeed(c)) continue; switch (state) { case PRE: if (debug) System.out.println("readXML: state PRE, c="+(char)c); if (c == '<') { // we are outside the root tag element stack.push(new Integer(TEXT)); state = START_TAG; } break; case START_TAG: if (debug) System.out.println("readXML: state START_TAG, c="+(char)c); state = popState(stack); if (c == '/') { stack.push(new Integer(state)); state = CLOSE_TAG; } else if (c == '?') { state = DOCTYPE; } else if (xml.getTag() != null) { in.unread(c); in.unread('<'); try { readXML(xml.addElement(null), in); } catch (myXMLException e) { throw e; } state = popState(stack); } else { stack.push(new Integer(state)); state = OPEN_TAG; sb.append((char)c); } break; case OPEN_TAG: if (debug) System.out.println("readXML: state OPEN_TAG, c="+(char)c); if (c == '>') { if (xml.getTag() == null) xml.setTag(sb.toString()); else xml.addElement(sb.toString()); sb.setLength(0); state = popState(stack); } else if (c == '/') { state = SINGLE_TAG; } else if (c == '-' && sb.toString().equals("!-")) { state = COMMENT; } else if (c == '[' && sb.toString().equals("![CDATA")) { state = CDATA; sb.setLength(0); } else if (c == 'E' && sb.toString().equals("!DOCTYP")) { sb.setLength(0); state = DOCTYPE; } else if (Character.isWhitespace((char)c)) { xml.setTag(sb.toString()); sb.setLength(0); state = IN_TAG; } else { sb.append((char)c); } break; case TEXT: if (Character.isWhitespace((char)c) && sb.length() < 1) break; if (debug) System.out.println("readXML: state TEXT, c="+(char)c); if (c == '<') { // we are currently reading a value and get the start of a tag stack.push(new Integer(state)); state = START_TAG; if (sb.length() > 0) { // we had a value, so the "<" must belong to a </close> xml.setValue(sb.toString()); sb.setLength(0); } } else if (c == '&') { // encoded items such as < stack.push(new Integer(state)); state = ENTITY; etag.setLength(0); } else { sb.append((char)c); } break; case CLOSE_TAG: if (debug) System.out.println("readXML: state CLOSE_TAG, c="+(char)c); if (c == '>') { // currently processing endtag like </tag> state = popState(stack); String endTag = sb.toString(); if (endTag.compareTo(xml.getTag()) != 0) throw new myXMLEncodingException("Mismatched close tag near line "+line+", column "+col); sb.setLength(0); if (debug) System.out.println("readXML: state CLOSE_TAG - Element complete...returning"); return; } else { sb.append((char)c); } break; case IN_TAG: if (debug) System.out.println("readXML: state IN_TAG, c="+(char)c); if (c == '>') { state = popState(stack); } else if (c == '/') { state = SINGLE_TAG; } else if (Character.isWhitespace((char)c)) { ; } else { state = ATTRIBUTE_LVALUE; sb.append((char)c); } break; case DONE: // due to recursion, this will never happen if (debug) System.out.println("readXML: state DONE, c="+(char)c); return; case CDATA: if (debug) System.out.println("readXML: state CDATA, c="+(char)c); if (c == '>' && sb.toString().endsWith("]]")) { sb.setLength(sb.length()-2); xml.setValue(sb.toString()); sb.setLength(0); state = popState(stack); } else { sb.append((char)c); } break; case COMMENT: // Inside <!-- ... --> if (debug) System.out.println("readXML: state COMMENT, c="+(char)c); if (c == '>' && sb.toString().endsWith("--")) { // found the end of a comment sb.setLength(0); state = popState(stack); } else { sb.append((char)c); } break; case DOCTYPE: if (debug) System.out.println("readXML: state DOCTYPE, c="+(char)c); if (c == '>') { // currently processing <? ... ?> state = popState(stack); if (state == TEXT) state = PRE; } break; case ENTITY: if (debug) System.out.println("readXML: state ENTITY, c="+(char)c); if (c == ';') { // we are completing an entity like >, < state = popState(stack); String cent = etag.toString(); etag.setLength(0); if (cent.equals("lt")) sb.append('<'); else if (cent.equals("gt")) // > = "<" sb.append('>'); else if (cent.equals("amp")) // & = "&" sb.append('&'); else if (cent.equals("quot")) // " = """ sb.append('"'); else if (cent.equals("apos")) // ' = "'" sb.append('\''); else if (cent.startsWith("#")) // ! = "!", ? = "?", / = "/", ; = ";" sb.append((char)Integer.parseInt(cent.substring(1))); else throw new myXMLEncodingException("Unknown entity: &"+cent+"; near line "+line+", column "+col); } else { etag.append((char)c); } break; case SINGLE_TAG: // Inside <tag/> if (debug) System.out.println("readXML: state SINGLE_TAG, c="+(char)c); if (xml.getTag() == null) { xml.setTag(sb.toString()); sb.setLength(0); } if (c != '>') throw new myXMLEncodingException("Expected > for tag <"+xml.getTag()+"/> near line "+line+", column "+col); return; case QUOTE: if (debug) System.out.println("readXML: state QUOTE, c="+(char)c); if (c == quotec) { rvalue = sb.toString(); sb.setLength(0); xml.Attribute.add(lvalue,rvalue); state = IN_TAG; // See section the XML spec, section 3.3.3 // on normalization processing. } else if (" \r\n\u0009".indexOf(c)>=0) { sb.append(' '); } else if (c == '&') { stack.push(new Integer(state)); state = ENTITY; etag.setLength(0); } else { sb.append((char)c); } break; case ATTRIBUTE_RVALUE: if (debug) System.out.println("readXML: state ATTRIBUTE_RVALUE, c="+(char)c); if (c == '"' || c == '\'') { quotec = c; state = QUOTE; } else if (Character.isWhitespace((char)c)) { ; } else { throw new myXMLEncodingException("Error in attribute processing near line "+line+", column "+col); } break; case ATTRIBUTE_LVALUE: if (debug) System.out.println("readXML: state ATTRIBUTE_LVALUE, c="+(char)c); if (Character.isWhitespace( (char) c)) { lvalue = sb.toString(); sb.setLength(0); state = ATTRIBUTE_EQUAL; } else if (c == '=') { lvalue = sb.toString(); sb.setLength(0); state = ATTRIBUTE_RVALUE; } else { sb.append( (char) c); } break; case ATTRIBUTE_EQUAL: if (debug) System.out.println("readXML: state ATTRIBUTE_EQUAL, c="+(char)c); if (c == '=') { state = ATTRIBUTE_RVALUE; } else if (Character.isWhitespace((char)c)) { ; } else { throw new myXMLEncodingException("Error in attribute processing near line "+line+", column "+col); } break; default: throw new myXMLEncodingException("State exception near line "+line+", column "+col); } } if (state == DONE) return; else throw new myXMLEncodingException("Missing end tag near line "+line+", column "+col); } /** * Signals that the XML being parsed has an error in the encoding. */ static public class myXMLEncodingException extends Exception implements Serializable { public myXMLEncodingException() { super(); } public myXMLEncodingException(String s) { super(s); } } /** * Signals that a general exception has occurred. */ static public class myXMLException extends Exception implements Serializable { public myXMLException() { super(); } public myXMLException(String s) { super(s); } } /** * Signals that an I/O excetion has occurred. */ static public class myXMLIOException extends Exception implements Serializable { public myXMLIOException() { super(); } public myXMLIOException(String s) { super(s); } } }